package concurrecy;

import java.util.concurrent.*;

class Toast {
    private State state = State.DRY;
    private static int count = 0;
    private final int id = count ++;

    public void butter() {
        System.out.println("butter " + this);
        state = State.BUTTERED;
    }

    public void jam() {
        System.out.println("jam " + this);
        state = State.JAMMED;
    }

    @Override
    public String toString() {
        return "Toast{" +
                "state=" + state +
                ", id=" + id +
                '}';
    }

    enum State {
        DRY, BUTTERED, JAMMED
    }
}

abstract class Maker implements Runnable {
    protected BlockingQueue<Toast> queue = new LinkedBlockingQueue<>();
    public BlockingQueue<Toast> getQueue() {
        return queue;
    }
}

class Toaster extends Maker {
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                TimeUnit.SECONDS.sleep(1);
                Toast toast = new Toast();
                System.out.println("make toast " + toast);
                queue.put(toast);
            }
        } catch(InterruptedException e) {
            System.out.println("toaster interrupted");
        }
    }
}

class Butter extends Maker {

    private BlockingQueue<Toast> upStream;

    public Butter(BlockingQueue<Toast> upStream) {
        this.upStream = upStream;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Toast to = upStream.take();
                to.butter();
                queue.put(to);
            }
        } catch(InterruptedException e) {
            System.out.println("butter interrupted");
        }
    }
}

class Jammer extends Maker {
    private BlockingQueue<Toast> upStream;

    public Jammer(BlockingQueue<Toast> upStream) {
        this.upStream = upStream;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                Toast to = upStream.take();
                to.jam();
                queue.put(to);
            }
        } catch (InterruptedException e) {
            System.out.println("jam interrupted");
        }
    }
}

class Consumer<T> implements Runnable {
    private BlockingQueue<T> upStream;

    public Consumer(BlockingQueue<T> upStream) {
        this.upStream = upStream;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                T to = upStream.take();
                System.out.println("eat toast " + to);
            }
        } catch(InterruptedException e) {
            System.out.println("consumer interrupted");
        }
    }
}

/**
 * 烤土司
 * 抹黄油 butter
 * 抹果酱 jam
 * 吃土司
 */
public class ToastDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = Executors.newCachedThreadPool();
        Toaster toaster = new Toaster();
        Butter butter = new Butter(toaster.getQueue());
        Jammer jammer = new Jammer(butter.getQueue());
        Consumer<Toast> consumer = new Consumer<>(jammer.getQueue());

        service.execute(toaster);
        service.execute(butter);
        service.execute(jammer);
        service.execute(consumer);

        TimeUnit.SECONDS.sleep(5);
        service.shutdownNow();
    }
}
